/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * $Id: AlarmData.cpp,v 1.8 2009-05-13 07:18:16 amaula Exp $
 *
 * \file
 * \brief Implementation for class CAlarmData
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#include "AlarmData.hpp"
#include "owndebug.h"

//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
using namespace MaCI::Alarm;
//*****************************************************************************
OWNDEBUG_INITIALIZE_MODULENUMBER(); // Initialize module print capability
//*****************************************************************************
//*****************************************************************************

std::string MaCI::Alarm::GetAlarmEventTypeStr(const EAlarmEventType &ae) 
{
  switch(ae) {
  case KEventTypeRaw:
    return std::string("Raw");
    
  case KEventTypeQuestion:
    return std::string("Question");
    
  case KEventTypeError:
    return std::string("Error");
    
  case KEventTypeWarning:
    return std::string("Warning");
    
  case KEventTypeNotification:
    return std::string("Notification");
    
  case KEventTypeInfo:
    return std::string("Info");
    
  case KEventTypeDebug:
    return std::string("Debug");
    
  case KEventTypeUnknown:
  default:
    return std::string("Unknown");
  }
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

CAlarmData::CAlarmData()
  : iTimestampPtr(NULL),
    iCommandPtr(NULL),
    iAlarmEventPtr(NULL),
    iTextPtr(NULL),
    iHeadingTextPtr(NULL),
    iSourceNameTextPtr(NULL)
{
}
//*****************************************************************************

CAlarmData::~CAlarmData()
{
}
//*****************************************************************************

CAlarmData::CAlarmData(const CAlarmData &aData)
  :  iTimestampPtr(NULL),
     iCommandPtr(NULL),
     iAlarmEventPtr(NULL),
     iTextPtr(NULL),
     iHeadingTextPtr(NULL),
     iSourceNameTextPtr(NULL)
{
  // Call assignment operator.
  *this = aData;
}
//*****************************************************************************

CAlarmData &CAlarmData::operator=(const CAlarmData &aData)
{
  // Reset
  Reset();

  // Create an internal BinBag instance
  if (CreateInternalBinBag(aData.iBinBagContainer) == false) {
    dPrint(4,"WARNING: Failed to create internal binbag for object %p from object %p",
           this, &aData);
  }
  
  // Do the decode on the buffer; it sets pointers and ... stuff :)
  if (DecodeFrom(NULL) == false) {
    dPrint(4,"Warning: Failed to Decode CAlarmData even though assignment operator used?");
  }
  
  return *this; 
}
//*****************************************************************************

bool CAlarmData::DecodeFrom(gim::binbag::CBinBag *aBinBag)
{
  bool result = true;
  const gim::binbag::TBinBlob *b = NULL;
  int previous_type = -1;
  
  // Store pointer and test it for NullNess
  if (aBinBag != NULL) 
    iBinBagContainer = aBinBag;

  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    while( (b = iBinBagContainer->GetNextBinary()) && result ) {
      switch(b->type) {
      case KTypeTimestamp:
	if (iTimestampPtr == NULL && 
            b->size == sizeof(Common::TTimestamp)) {
	  iTimestampPtr = reinterpret_cast<const Common::TTimestamp *>(b->data);

	} else result = false;
	break;

      case KTypeCommand: {
        if (iCommandPtr == NULL && 
            b->size == sizeof(TCommand)) {
          iCommandPtr = reinterpret_cast<const TCommand *>(b->data); 

        } else result = false;
        break;
      }
      case KTypeAlarmEvent: {
        if (iAlarmEventPtr == NULL && 
            b->size == sizeof(TAlarmEvent)) {
          iAlarmEventPtr = reinterpret_cast<const TAlarmEvent *>(b->data); 

        } else result = false;
        break;
      }
      case KTypeText: {
        if (iTextPtr == NULL && 
            b->size >= sizeof(TText) &&
            ( previous_type == KTypeAlarmEvent ||
              previous_type == KTypeHeadingText ) ) {
          iTextPtr = reinterpret_cast<const TText *>(b->data); 
          
          // Check size validity (required on variable size fields)
          if (iTextPtr->length + sizeof(TText) != b->size) {
            iTextPtr = NULL;
            result = false;
          }

        } else result = false;
        break;
      }

      case KTypeHeadingText: {
        if (iHeadingTextPtr == NULL && 
            b->size >= sizeof(THeadingText) &&
            previous_type == KTypeAlarmEvent) {
          iHeadingTextPtr = reinterpret_cast<const THeadingText *>(b->data); 
          
          // Check size validity (required on variable size fields)
          if (iHeadingTextPtr->length + sizeof(THeadingText) != b->size) {
            iHeadingTextPtr = NULL;
            result = false;
          }

        } else result = false;
        break;
      }

      case KTypeSourceNameText: {
        if (iSourceNameTextPtr == NULL && 
            b->size >= sizeof(TSourceNameText)) {
          iSourceNameTextPtr = reinterpret_cast<const TSourceNameText *>(b->data); 
          
          // Check size validity (required on variable size fields)
          if (iSourceNameTextPtr->length + sizeof(TSourceNameText) != b->size) {
            iSourceNameTextPtr = NULL;
            result = false;
          }
          
        } else result = false;
        break;
      }

      default:
	// Unknown Type in BinBag. Do nothing.
	dPrint(10,"Unknown type %u, ignoring.", b->type);
        break;
      }
      
      // Store previous type
      previous_type = b->type;
    }
  } else result = false;

  // Store the result
  SetReadOnlyContainer(result);

  return result;
}
//*****************************************************************************

void CAlarmData::Reset()
{
  CMaCIData::Reset();

  // Clean out. (Same values as in default constructor)
  iTimestampPtr = NULL;
  iCommandPtr = NULL;
  iAlarmEventPtr = NULL;
  iTextPtr = NULL;
  iHeadingTextPtr = NULL;
  iSourceNameTextPtr = NULL;
}
//*****************************************************************************

void CAlarmData::Print(const int l) const
{
  const gim::binbag::TBinBlob *b = NULL;
  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    while( (b = iBinBagContainer->GetNextBinary())) {
      switch(b->type) {
      case KTypeTimestamp: {
	const Common::TTimestamp *ts = 
          reinterpret_cast<const Common::TTimestamp *>(b->data);
	dPrint(l,"TTimestamp: s=%d, us=%d", 
	       ts->timestamp_s,
	       ts->timestamp_us);
	break;
      }
        
      case KTypeCommand: {
	const TCommand *ts = 
          reinterpret_cast<const TCommand *>(b->data);
	dPrint(l,"TCommand: cmd=%d, data=%d", 
	       ts->cmd, ts->data);
	break;
      }
	
      case KTypeAlarmEvent: {
	const TAlarmEvent *ae = 
          reinterpret_cast<const TAlarmEvent *>(b->data);
	dPrint(l,"TAlarmEvent: type=%d, timeout_s=%d, flags=%d, id=%d",
               ae->type,
               ae->timeout_s,
               ae->flags,
               ae->id);
	break;
      }
        
      case KTypeText: {
	const TText *t = 
          reinterpret_cast<const TText *>(b->data);
	dPrint(l,"TText: length=%d, text='%s'",
               t->length,
               t->text);
	break;
      }

      case KTypeHeadingText: {
	const THeadingText *t = 
          reinterpret_cast<const THeadingText *>(b->data);
	dPrint(l,"THeadingText: length=%d, text='%s'",
               t->length,
               t->text);
	break;
      }

      case KTypeSourceNameText: {
	const TSourceNameText *t = 
          reinterpret_cast<const TSourceNameText *>(b->data);
	dPrint(l,"TSourceNameText: length=%d, text='%s'",
               t->length,
               t->text);
	break;
      }
	
      default:
	// Unknown Type in BinBag. Do nothing.
	break;
      }
    }
  }
}
//*****************************************************************************

bool CAlarmData::SetTimestamp(const Common::TTimestamp &aStamp)
{
  bool result = false;
  if (!IsReadOnlyContainer() && iTimestampPtr == NULL) {
    ADDELEMENT(Common::TTimestamp, KTypeTimestamp, iTimestampPtr, aStamp);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CAlarmData::SetCommand(const TCommand &aCmd)
{  
  bool result = false;
  if (!IsReadOnlyContainer() && iCommandPtr == NULL) {
    ADDELEMENT(TCommand, KTypeCommand, iCommandPtr, aCmd);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CAlarmData::SetAlarmEvent(const TAlarmEvent &aEvent)
{  
  bool result = false;
  if (!IsReadOnlyContainer() && iAlarmEventPtr == NULL) {
    ADDELEMENT(TAlarmEvent, KTypeAlarmEvent, iAlarmEventPtr, aEvent);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CAlarmData::SetText(const std::string &aText)
{
  bool result = false;
  
  if (!IsReadOnlyContainer() && iTextPtr == NULL && 
      ( GetLastElementType() == KTypeAlarmEvent ||
        GetLastElementType() == KTypeHeadingText ) ) {
    // Storage
    const unsigned int bufferSize = aText.size() + sizeof(TText) + 1;
    unsigned char buffer[bufferSize];
    TText *tHdr = (TText *)buffer;

    // Data assign
    tHdr->length = aText.size() + 1;
    memcpy(tHdr->text, aText.c_str(), aText.size() + 1);
    
    // Insertion.
    const gim::binbag::TBinBlob *b = 
      iBinBagContainer->AddBinary(KTypeText, 
                                  (const char *)buffer, 
                                  bufferSize);
    iTextPtr = reinterpret_cast<const TText *>(b->data);
    SetLastElementType(KTypeText);
    result = true;
  }

  return result;
}
//*****************************************************************************

bool CAlarmData::SetHeadingText(const std::string &aText)
{
  bool result = false;
  
  if (!IsReadOnlyContainer() && 
      iHeadingTextPtr == NULL && GetLastElementType() == KTypeAlarmEvent) {
    // Storage
    const unsigned int bufferSize = aText.size() + sizeof(TText) + 1;
    unsigned char buffer[bufferSize];
    THeadingText *tHdr = (THeadingText *)buffer;
    
    // Data assign
    tHdr->length = aText.size() + 1;
    memcpy(tHdr->text, aText.c_str(), aText.size() + 1);
    
    // Insertion.
    const gim::binbag::TBinBlob *b = 
      iBinBagContainer->AddBinary(KTypeHeadingText, 
                                  (const char *)buffer, 
                                  bufferSize);
    iHeadingTextPtr = reinterpret_cast<const THeadingText *>(b->data);
    SetLastElementType(KTypeHeadingText);
    result = true;
  }

  return result; 
}
//*****************************************************************************

bool CAlarmData::SetSourceNameText(const std::string &aText)
{
  bool result = false;
  
  if (!IsReadOnlyContainer() && 
      iSourceNameTextPtr == NULL) {
    // Storage
    const unsigned int bufferSize = aText.size() + sizeof(TSourceNameText) + 1;
    unsigned char buffer[bufferSize];
    TSourceNameText *tHdr = (TSourceNameText *)buffer;
    
    // Data assign
    tHdr->length = aText.size() + 1;
    memcpy(tHdr->text, aText.c_str(), aText.size() + 1);
    
    // Insertion.
    const gim::binbag::TBinBlob *b = 
      iBinBagContainer->AddBinary(KTypeSourceNameText, 
                                  (const char *)buffer, 
                                  bufferSize);
    iSourceNameTextPtr = reinterpret_cast<const TSourceNameText *>(b->data);
    SetLastElementType(KTypeSourceNameText);
    result = true;
  }

  return result; 
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

const MaCI::Common::TTimestamp *CAlarmData::GetTimestamp(void) const
{
  return iTimestampPtr;
}
//*****************************************************************************

const TCommand *CAlarmData::GetCommand(void) const
{
  return iCommandPtr;
}
//*****************************************************************************

const TAlarmEvent *CAlarmData::GetAlarmEvent(void) const
{
  return iAlarmEventPtr;
}
//*****************************************************************************

const TText *CAlarmData::GetText(void) const
{
  return iTextPtr;
}
//*****************************************************************************

const THeadingText *CAlarmData::GetHeadingText(void) const
{
  return iHeadingTextPtr;
}
//*****************************************************************************

const THeadingText *CAlarmData::GetSourceNameText(void) const
{
  return iSourceNameTextPtr;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

#ifdef COMPILE_ALARMDATA_STANDALONE_MAIN
#include <string>

int main(void)
{
  using namespace MaCI::Alarm;
  gim::binbag::CBinBag bb;
  CAlarmData A;
  CAlarmData B;
  bool r;

  printf("Carry out Encoding/Decoding test.\n");

  printf("Encoding...\n");
  // Do Encoding 
  A.EncodeTo(&bb);
  
  printf("Encode: Timestamp(1,2) ... ");
  r = A.SetTimestamp(MaCI::Common::TTimestamp(1, 2));
  assert(r == true);
  printf("OK.\n");

  printf("Encode: TCommand(KCommandGetInformation(4)) ... ");
  r = A.SetCommand(TCommand(KCommandClientKeepalive));
  assert(r == true);
  printf("OK.\n");

  printf("Encode: TAlarmEvent(KEventTypeQuestion(2), 10, 0, 666) ... ");
  r = A.SetAlarmEvent(TAlarmEvent(TAlarmEvent(KEventTypeQuestion, 10, 0, 666)));
  assert(r == true);
  printf("OK.\n");

  printf("Encode: TText('Are you sure?') ... ");
  r = A.SetText("Are you sure?");
  assert(r == true);
  printf("OK.\n");

  //////////////////////////////////////////////////////////
  printf("Decoding...\n");
  // Do Decoding
  B.DecodeFrom(&bb);
  printf("Decode: GetTimestamp() = (%d, %d)\n", 
	 B.GetTimestamp()->timestamp_s, 
	 B.GetTimestamp()->timestamp_us);
  assert(B.GetTimestamp()->timestamp_s == 1);
  assert(B.GetTimestamp()->timestamp_us == 2);
   

  printf("Decode: GetCommand() == (%d)\n", 
         B.GetCommand()->cmd);
  assert(B.GetCommand()->cmd == KCommandClientKeepalive);


  printf("Decode: GetAlarmEvent() == (%d, %d, %d, %d)\n", 
         B.GetAlarmEvent()->type,
         B.GetAlarmEvent()->timeout_s,
         B.GetAlarmEvent()->flags,
         B.GetAlarmEvent()->id);
  assert(B.GetAlarmEvent()->type == KEventTypeQuestion);
  assert(B.GetAlarmEvent()->timeout_s == 10);
  assert(B.GetAlarmEvent()->flags == 0);
  assert(B.GetAlarmEvent()->id == 666);

  printf("Decode: GetText() == ('%s')\n", 
         B.GetText()->text);
  assert(strcmp(B.GetText()->text, "Are you sure?") == 0);

  


  printf("Test complete.\n");
  

}
#endif
//*****************************************************************************
//*****************************************************************************
